package com.demo.webflux.spring5core.c7.servlet.dispatch;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.demo.webflux.spring5core.c7.anno.Autowired;
import com.demo.webflux.spring5core.c7.anno.Controller;
import com.demo.webflux.spring5core.c7.anno.RequestMapping;
import com.demo.webflux.spring5core.c7.anno.RequestParam;
import com.demo.webflux.spring5core.c7.anno.Service;
import com.demo.webflux.spring5core.c7.servlet.domain.HttpServletRequest;
import com.demo.webflux.spring5core.c7.servlet.domain.HttpServletResponse;
import com.demo.webflux.spring5core.c7.servlet.domain.ServletConfig;

public class DispatchServlet {

	// 保存配置文件内容
	private Properties contextConfig = new Properties();
	// 保存扫描的所有类名
	private List<String> classNames = new ArrayList<>();
	// ioc容器
	private Map<String, Object> ioc = new HashMap<>();
	// 保存url和method的映射关系
	private List<Handler> handlerMapping = new ArrayList<>();

	public void doGet(HttpServletRequest request, HttpServletResponse resp) throws Exception {
		doPost(request, resp);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse resp) throws Exception {
		doDispatch(request, resp);
	}

	private void doDispatch(HttpServletRequest request, HttpServletResponse resp) throws Exception {
		Handler handler = getHandler(request);
		if(handler == null) {
			resp.getWriter().write("404 not found!");
			return;
		}

		// 获取方法参数
		Class<?>[] paramTypes = handler.method.getParameterTypes();
		Object[] paramValues = new Object[paramTypes.length];
		Map<String, String[]> params = request.getParameterMap();
		for (Map.Entry<String, String[]> parm : params.entrySet()) {
			String value = Arrays.toString(parm.getValue()).replaceAll("\\[|\\]", "").replaceAll("\\s", ",");
			if(!handler.paramIndexMapping.containsKey(parm.getKey())) {
				continue;
			}
			int index = handler.paramIndexMapping.get(parm.getKey());
			paramValues[index] = convert(paramTypes[index], value);
		}

		if(handler.paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
			int index = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
			paramValues[index] = request;
		}
		if(handler.paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
			int index = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
			paramValues[index] = resp;
		}

		Object returnValue = handler.method.invoke(handler.controller, paramValues);
		if(returnValue == null || returnValue instanceof Void) {
			return;
		}
		resp.getWriter().write(returnValue.toString());
	}

	// url传过来的都是string类型的，由于http基于字符串协议，只需要把string转换为任意类型
	private Object convert(Class<?> type, String value) {
		if(Integer.class == type) {
			return Integer.valueOf(value);
		}
		// 如果还有其他类型，继续处理
		return value;
	}

	private Handler getHandler(HttpServletRequest request) {
		if(handlerMapping.isEmpty()) {
			return null;
		}
		String url = request.getRequestURI();
		String contextPath = request.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		for (Handler handler : handlerMapping) {
			Matcher matcher = handler.pattern.matcher(url);
			if(!matcher.matches()) {
				continue;
			}
			return handler;
		}
		return null;
	}

	public void init(ServletConfig config) {
		// 加载配置文件
		doLoadConfig(config.getInitParamter("contextConfigLocation"));
		// 扫描相关的类
		doScanner(contextConfig.getProperty("scanPackage"));
		// 初始化扫描到的类，并且将它们放入ioc容器
		doInstance();
		// 完成依赖注入
		doAutowired();
		// 初始化HandlerMapping
		initHandlerMapping();
	}

	private void initHandlerMapping() {
		if(ioc.isEmpty()) {
			return;
		}
		for(Map.Entry<String, Object> entry : ioc.entrySet()) {
			Class<?> clazz = entry.getValue().getClass();
			if(!clazz.isAnnotationPresent(Controller.class)) {
				continue;
			}
			String url = "";
			// 获取controller上的RequestMapping
			if(clazz.isAnnotationPresent(RequestMapping.class)) {
				RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
				url = requestMapping.value();
			}
			// 获取method的url配置
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if(!method.isAnnotationPresent(RequestMapping.class)) {
					continue;
				}
				RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
				String regex = ("/" + url + requestMapping.value()).replaceAll("/+", "/");
				Pattern pattern = Pattern.compile(regex);
				handlerMapping.add(new Handler(pattern, entry.getValue(), method));
			}
		}
	}

	private void doAutowired() {
		if(ioc.isEmpty()) {
			return;
		}
		for(Map.Entry<String, Object> entry : ioc.entrySet()) {
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			for (Field field : fields) {
				if(!field.isAnnotationPresent(Autowired.class)) {
					continue;
				}
				Autowired autowired = field.getAnnotation(Autowired.class);

				String beanName = autowired.value().trim();
				if("".equals(beanName)) {
					beanName = toLowerFirstCase(field.getType().getSimpleName());
				}
				field.setAccessible(true);
				try {
					field.set(entry.getValue(), ioc.get(beanName));
				} catch (IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private void doInstance() {
		if(classNames.isEmpty()) {
			return;
		}

		try {
			for (String className : classNames) {
				Class<?> clazz = Class.forName(className);

				if(clazz.isAnnotationPresent(Controller.class)) {
					Object instancec = clazz.newInstance();
					String beanName = toLowerFirstCase(clazz.getSimpleName());
					ioc.put(beanName, instancec);
				} else if(clazz.isAnnotationPresent(Service.class)) {
					Service service = clazz.getAnnotation(Service.class);
					String beanName = service.value();
					if("".equals(beanName.trim())) {
						beanName = toLowerFirstCase(clazz.getSimpleName());
					}

					Object instance = clazz.newInstance();
					ioc.put(beanName, instance);
					// 根据接口赋值，这是投机取巧的方式
					for(Class<?> i : clazz.getInterfaces()) {
						if(ioc.containsKey(i.getName())) {
							throw new Exception("The " + i.getName() + " is exists!");
						}
						// 把接口的类型直接当成key
						ioc.put(i.getName(), instance);
					}
				} else {
					continue;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private String toLowerFirstCase(String simpleName) {
		char[] chars = simpleName.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}

	private void doScanner(String scanPackage) {
		URL url = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.", "/"));
		File classPath = new File(url.getFile());
		for(File file : classPath.listFiles()) {
			if(file.isDirectory()) {
				doScanner(scanPackage + "." + file.getName());
			} else {
				if(!file.getName().endsWith(".class")) {
					continue;
				}
				String className = (scanPackage + "." + file.getName().replace(".class", ""));
				classNames.add(className);
			}
		}
	}

	private void doLoadConfig(String contextConfigLocation) {
		InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
		try {
			contextConfig.load(fis);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(null != fis) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private class Handler {

		protected Object controller; // 方法对应的实例
		protected Method method; // 映射的方法
		protected Pattern pattern;
		protected Map<String, Integer> paramIndexMapping; // 参数顺序

		public Handler(Pattern pattern, Object controller, Method method) {
			this.pattern = pattern;
			this.controller = controller;
			this.method = method;
			paramIndexMapping = new HashMap<>();
			putParamIndexMapping(method);
		}

		private void putParamIndexMapping(Method method2) {
			// 提取方法中加了注解的参数
			Annotation[][] pa = method.getParameterAnnotations();
			for (int i=0; i<pa.length; i++) {
				for (Annotation a : pa[i]) {
					if(a instanceof RequestParam) {
						String paramName = ((RequestParam)a).value();
						if(!"".equals(paramName.trim())) {
							paramIndexMapping.put(paramName, i);
						}
					}
				}
			}
			// 提取方法中request和response参数
			Class<?>[] paramsTypes = method.getParameterTypes();
			for (int i = 0; i < paramsTypes.length; i++) {
				Class<?> type = paramsTypes[i];
				if(type == HttpServletRequest.class || type == HttpServletResponse.class) {
					paramIndexMapping.put(type.getName(), i);
				}
			}
		}

	}

}
